package qos.estimator.fuzzy;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;


import fuzzy.EvaluationException;
import fuzzy.FuzzyBlockOfRules;
import fuzzy.FuzzyEngine;
import fuzzy.LinguisticVariable;
import fuzzy.NoRulesFiredException;
import fuzzy.RulesParsingException;

public class FuzzyEstimator {

	private FuzzyBlockOfRules blockOfRules;
	
	private RuleGenerator ruleGenerator;


	LinguisticVariable memoryPercentual = new LinguisticVariable("memoryPercentual");
	LinguisticVariable cpuSoftPercentual = new LinguisticVariable("cpuSoftPercentual");
	LinguisticVariable cpuIOWaitPercentual = new LinguisticVariable("cpuIOWaitPercentual");
	LinguisticVariable cpuUsrPercentual = new LinguisticVariable("cpuUsrPercentual");
	LinguisticVariable cpuSysPercentual = new LinguisticVariable("cpuSysPercentual");
	LinguisticVariable residentSetSize = new LinguisticVariable("residentSetSize");
	LinguisticVariable virtualSize = new LinguisticVariable("virtualSize");
	LinguisticVariable majorFaultsPerSecond = new LinguisticVariable("majorFaultsPerSecond");
	LinguisticVariable minorFaultsPerSecond = new LinguisticVariable("minorFaultsPerSecond");
	LinguisticVariable interruptionsPerSecond = new LinguisticVariable("interruptionsPerSecond");
	LinguisticVariable diskUsage = new LinguisticVariable("diskUsage");
	LinguisticVariable elapsedTime = new LinguisticVariable("elapsedTime");
	LinguisticVariable concSessions = new LinguisticVariable( "concSessions");
	LinguisticVariable buffers     = new LinguisticVariable( "buffers");
	LinguisticVariable locks = new LinguisticVariable( "locks");
	
	private FuzzyEngine fuzzyEngine = new FuzzyEngine();
	private Set<String> initialRuleSet = new HashSet<String>();
	private ConfigureSystemCommand configureSystemCommand = new LabGCCBD5FuzzyRegionsSystemCommand();

	public FuzzyEngine getFuzzyEngine() {
		return fuzzyEngine;
	}

	public void setFuzzyEngine(FuzzyEngine fuzzyEngine) {
		this.fuzzyEngine = fuzzyEngine;
	}
	
	public Collection<String> getInitialRuleSet() {
		return initialRuleSet;
	}

	public void setInitialRuleSet(Collection<String> initialRuleSet) {
		for (String rule : initialRuleSet) {
			this.initialRuleSet.add(rule);				
		}
	}
	
	public FuzzyBlockOfRules getBlockOfRules() {
		return blockOfRules;
	}

	public RuleGenerator getRuleGenerator() {
		return ruleGenerator;
	}
	
	public FuzzyEstimator(boolean initializeEmptyKnowledgeBase) {
		configureSystemCommand.configureSystem(this);
		initialize(initializeEmptyKnowledgeBase);
		System.out.println("System configured and ready...");
	}
	
	public String getMemoryPercentualLabel(double value){
		return getLinguisticVariableLabel(memoryPercentual, value);
	}
	
	public String getCpuSoftPercentualLabel(double value){
		return getLinguisticVariableLabel(cpuSoftPercentual, value);
	}

	public String getCpuIOWaitPercentualLabel(double value){
		return getLinguisticVariableLabel(cpuIOWaitPercentual, value);
	}
	
	public String getCpuUsrPercentualLabel(double value){
		return getLinguisticVariableLabel(cpuUsrPercentual, value);
	}
	

	public String getResidentSetSizeLabel(double value){
		return getLinguisticVariableLabel(residentSetSize, value);
	}
	
	public String getVirtualSizeLabel(double value){
		return getLinguisticVariableLabel(virtualSize, value);
	}
	
	public String getMajorFaultsPerSecondLabel(double value){
		return getLinguisticVariableLabel(majorFaultsPerSecond, value);
	}

	public String getMinorFaultsPerSecondLabel(double value){
		return getLinguisticVariableLabel(minorFaultsPerSecond, value);
	}

	public String getInterruptionsPerSecondLabel(double value){
		return getLinguisticVariableLabel(interruptionsPerSecond, value);
	}
	
	public String getDiskUsageLabel(double value){
		return getLinguisticVariableLabel(diskUsage, value);
	}
	
	public String getCpuSysPercentualLabel(double value){
		return getLinguisticVariableLabel(cpuSysPercentual, value);
	}
	
	public String getElapsedTimeLabel(double value){
		return getLinguisticVariableLabel(elapsedTime, value);
	}
	
	public String getConcSessionsLabel(double value){
		return getLinguisticVariableLabel( concSessions, value);
	}
	
	public String getLocksLabel(double value){
		return getLinguisticVariableLabel( locks, value);
	}

	public String getBuffersLabel(double value){
		return getLinguisticVariableLabel( buffers, value);
	}
	
	public String getLinguisticVariableLabel(LinguisticVariable linguisticVariable, double value){
		linguisticVariable.setInputValue(value);
		String higherMembershipFunction  = "low";
		double higherDegree = linguisticVariable.is("low");
		
		if(higherDegree < linguisticVariable.is("middle-low")){
			higherMembershipFunction = "middle-low";
			higherDegree = linguisticVariable.is("middle-low");
		}
		
		if(higherDegree < linguisticVariable.is("middle")){
			higherMembershipFunction = "middle";
			higherDegree = linguisticVariable.is("middle");
		}

		if(higherDegree < linguisticVariable.is("middle-high")){
			higherMembershipFunction = "middle-high";
			higherDegree = linguisticVariable.is("middle-high");
		}

		if(higherDegree < linguisticVariable.is("high")){
			higherMembershipFunction  = "high";
			higherDegree = linguisticVariable.is("high");
		}

		return higherMembershipFunction ;
	}
	
	
	public void initialize(boolean initializeEmptyKnowledgeBase) {
		ruleGenerator = new RuleGenerator(this);
		if(initializeEmptyKnowledgeBase){
			return;
		}

		for (String rule : ruleGenerator.generateRulesFromFile()) {
			initialRuleSet.add(rule);
		}
		initializeFuzzyBlock(); 
	}


	
	public String getExpectedElapsedTimeForQuery(ClassificationTuple ct ) throws NoRulesFiredException {
		
		if(blockOfRules == null)
			throw new NoRulesFiredException("Block of rules not configured.");
		
		memoryPercentual.setInputValue(ct.getMemoryPercentual());
		cpuSoftPercentual.setInputValue(ct.getCpuSoftPercentual());
		cpuIOWaitPercentual.setInputValue(ct.getCpuIOWaitPercentual());
		cpuUsrPercentual.setInputValue(ct.getCpuUsrPercentual());
		cpuSysPercentual.setInputValue(ct.getCpuSysPercentual());
		residentSetSize.setInputValue(ct.getResidentSetSize());
		virtualSize.setInputValue(ct.getVirtualSize());
		majorFaultsPerSecond.setInputValue(ct.getMajorFaultsPerSecond());
		minorFaultsPerSecond.setInputValue(ct.getMinorFaultsPerSecond());
		interruptionsPerSecond.setInputValue(ct.getInterruptionsPerSecond());
		diskUsage.setInputValue(ct.getDiskUsage());
		concSessions.setInputValue(ct.getConcSessions());
		buffers.setInputValue(ct.getBuffers());
		locks.setInputValue(ct.getLocks());

		//elapsedTime.reset();
		try {
			blockOfRules.evaluateBlock();
			double result = elapsedTime.defuzzify();
			elapsedTime.setInputValue(result);
			return getElapsedTimeLabel(result);
		} catch (EvaluationException e) {
			e.printStackTrace();
		}
		
		return null;
		
		
	}

	public void addRule(ClassificationTuple currentState) {
		ruleGenerator.setValues(currentState);
		blockOfRules = new FuzzyBlockOfRules(getRulesAsString());
		fuzzyEngine.register(blockOfRules);
		try {
			blockOfRules.parseBlock();
		} catch (RulesParsingException e) {
			e.printStackTrace();
		}
	}
	
	private String getRulesAsString(){
		String rules = "";
		for (String rule : ruleGenerator.rules) {
			rules += rule+"\n";
		}
		
		return rules;
	}
	

	public void inputStatistics(Collection<String> trainingStatistics) {
		for(String rule : ruleGenerator.generateRulesFromCollection(trainingStatistics)){
			initialRuleSet.add(rule);
		}
		initializeFuzzyBlock(); 
	}

	private void initializeFuzzyBlock() {
		try {
			String rules = getRulesAsString();
			blockOfRules = new FuzzyBlockOfRules(rules);
			fuzzyEngine.register(blockOfRules);
			blockOfRules.parseBlock();

		} catch (RulesParsingException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) throws Exception{

		FuzzyEstimator fuzzyEstimator = new FuzzyEstimator(false);
		
		ClassificationTuple classificationTuple = new ClassificationTuple();
		classificationTuple.setBuffers(2679396.33333333);
		classificationTuple.setConcSessions(11.3333333333);
		classificationTuple.setCpuIOWaitPercentual(49.5933333333);
		classificationTuple.setCpuSoftPercentual(0.44);
		classificationTuple.setCpuSysPercentual(4.25);
		classificationTuple.setCpuUsrPercentual(1.68);
		classificationTuple.setDiskUsage(267731.2);
		classificationTuple.setInterruptionsPerSecond(1059.3333333333);
		classificationTuple.setLocks(26.6666666667);
		classificationTuple.setMemoryPercentual(2179132.75);
		classificationTuple.setMajorFaultsPerSecond(381.07);
		classificationTuple.setMinorFaultsPerSecond(90.6);
		classificationTuple.setResidentSetSize(1009212.66666667);
		classificationTuple.setVirtualSize(7095902);
		classificationTuple.setElapsedTime(40000);

		fuzzyEstimator.addRule(classificationTuple);
		
		classificationTuple.setBuffers(2679396.33333333);
		classificationTuple.setConcSessions(11.3333333333);
		classificationTuple.setCpuIOWaitPercentual(49.5933333333);
		classificationTuple.setCpuSoftPercentual(0.44);
		classificationTuple.setCpuSysPercentual(4.25);
		classificationTuple.setCpuUsrPercentual(1.68);
		classificationTuple.setDiskUsage(267731.2);
		classificationTuple.setInterruptionsPerSecond(1059.3333333333);
		classificationTuple.setLocks(26.6666666667);
		classificationTuple.setMemoryPercentual(2.75);
		classificationTuple.setMajorFaultsPerSecond(381.07);
		classificationTuple.setMinorFaultsPerSecond(90.6);
		classificationTuple.setResidentSetSize(1009212.66666667);
		classificationTuple.setVirtualSize(7095902);
		classificationTuple.setElapsedTime(40000);
		

		System.out.println(fuzzyEstimator.getExpectedElapsedTimeForQuery(classificationTuple));
	}

}
